home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 7 / Night Owl Shareware (NOPV7)(Night Owl Publisher Inc.)(1992).bin / 038a / bash1_12.arj / BASH1-12.TAR / bash-1.12 / builtins / cd.def < prev    next >
Text File  |  1991-12-31  |  15KB  |  589 lines

  1. This file is cd.def, from which is created cd.c.  It implements the
  2. builtins "cd", "pwd", "pushd", "popd", and "dirs" in Bash.
  3.  
  4. Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
  5.  
  6. This file is part of GNU Bash, the Bourne Again SHell.
  7.  
  8. Bash is free software; you can redistribute it and/or modify it under
  9. the terms of the GNU General Public License as published by the Free
  10. Software Foundation; either version 1, or (at your option) any later
  11. version.
  12.  
  13. Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  14. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16. for more details.
  17.  
  18. You should have received a copy of the GNU General Public License along
  19. with Bash; see the file COPYING.  If not, write to the Free Software
  20. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. $PRODUCES cd.c
  23.  
  24. #include <stdio.h>
  25. #include <sys/param.h>
  26. #include "../shell.h"
  27. #include "../maxpath.h"
  28.  
  29. static int change_to_directory (), cd_to_string ();
  30.  
  31. /* Keeps track of the current working directory. */
  32. extern char *the_current_working_directory;
  33.  
  34. /* By default, follow the symbolic links as if they were real directories
  35.    while hacking the `cd' command.  This means that `cd ..' moves up in
  36.    the string of symbolic links that make up the current directory, instead
  37.    of the absolute directory.  The shell variable `nolinks' controls this
  38.    flag. */
  39. int follow_symbolic_links = 1;
  40.  
  41. $BUILTIN cd
  42. $FUNCTION cd_builtin
  43. $SHORT_DOC cd [dir]
  44. Change the current directory to DIR.  The variable $HOME is the
  45. default DIR.  The variable $CDPATH defines the search path for
  46. the directory containing DIR.  Alternative directory names are
  47. separated by a colon (:).  A null directory name is the same as
  48. the current directory, i.e. `.'.  If DIR begins with a slash (/),
  49. then $CDPATH is not used.  If the directory is not found, and the
  50. shell variable `cdable_vars' exists, then try the word as a variable
  51. name.  If that variable has a value, then cd to the value of that
  52. variable.
  53. $END
  54.  
  55. int
  56. cd_builtin (list)
  57.      WORD_LIST *list;
  58. {
  59.   char *dirname;
  60.  
  61.   {
  62.     extern int restricted;
  63.     if (restricted)
  64.       {
  65.     builtin_error ("Privileged command");
  66.     return (EXECUTION_FAILURE);
  67.       }
  68.   }
  69.  
  70.   if (list)
  71.     {
  72.       char *extract_colon_unit ();
  73.       char *path_string = get_string_value ("CDPATH");
  74.       char *path;
  75.       int index = 0;
  76.  
  77.       dirname = list->word->word;
  78.  
  79.       if (path_string && !absolute_pathname (dirname))
  80.     {
  81.       while ((path = extract_colon_unit (path_string, &index)))
  82.         {
  83.           char *dir;
  84.  
  85.           if (*path && (*path == '~'))
  86.         {
  87.           char *tilde_expand (), *te_string = tilde_expand (path);
  88.  
  89.           free (path);
  90.           path = te_string;
  91.         }
  92.  
  93.           if (!*path)
  94.         {
  95.           free (path);
  96.           path = savestring ("."); /* by definition. */
  97.         }
  98.  
  99.           dir = (char *)alloca (2 + strlen (dirname) + strlen (path));
  100.  
  101.           if (!dir)
  102.         fatal_error ("Out of memory");
  103.  
  104.           strcpy (dir, path);
  105.           if (path[strlen (path) - 1] != '/')
  106.         strcat (dir, "/");
  107.           strcat (dir, dirname);
  108.           free (path);
  109.  
  110.           if (change_to_directory (dir))
  111.         {
  112.           if (strncmp (dir, "./", 2) != 0)
  113.             printf ("%s\n", dir);
  114.           dirname = dir;
  115.  
  116.           goto bind_and_exit;
  117.         }
  118.         }
  119.     }
  120.  
  121.       if (!change_to_directory (dirname))
  122.     {
  123.       /* Maybe this is `cd -', equivalent to `cd $OLDPWD' */
  124.       if (dirname[0] == '-' && dirname[1] == '\0')
  125.         {
  126.           char *t = get_string_value ("OLDPWD");
  127.  
  128.           if (t && change_to_directory (t))
  129.         goto bind_and_exit;
  130.         }
  131.  
  132.       /* If the user requests it, then perhaps this is the name of
  133.          a shell variable, whose value contains the directory to
  134.          change to.  If that is the case, then change to that
  135.          directory. */
  136.       if (find_variable ("cdable_vars"))
  137.         {
  138.           char *t = get_string_value (dirname);
  139.  
  140.           if (t && change_to_directory (t))
  141.         {
  142.           printf ("%s\n", t);
  143.           goto bind_and_exit;
  144.         }
  145.         }
  146.  
  147.       file_error (dirname);
  148.       return (EXECUTION_FAILURE);
  149.     }
  150.       goto bind_and_exit;
  151.     }
  152.   else
  153.     {
  154.       dirname = get_string_value ("HOME");
  155.  
  156.       if (!dirname)
  157.     return (EXECUTION_FAILURE);
  158.  
  159.       if (!change_to_directory (dirname))
  160.     {
  161.       file_error (dirname);
  162.       return (EXECUTION_FAILURE);
  163.     }
  164.  
  165.     bind_and_exit:
  166.       {
  167.     char *get_working_directory (), *get_string_value ();
  168.     char *directory = get_working_directory ("cd");
  169.  
  170.     bind_variable ("OLDPWD", get_string_value ("PWD"));
  171.     bind_variable ("PWD", directory);
  172.  
  173.     if (directory)
  174.       free (directory);
  175.       }
  176.       return (EXECUTION_SUCCESS);
  177.     }
  178. }
  179.  
  180. $BUILTIN pwd
  181. $FUNCTION pwd_builtin
  182. $SHORT_DOC pwd
  183. Print the current working directory.
  184. $END
  185.  
  186. /* Non-zero means that pwd always give verbatim directory, regardless of
  187.    symbolic link following. */
  188. static int verbatim_pwd = 1;
  189.  
  190. /* Print the name of the current working directory. */
  191. pwd_builtin (list)
  192.      WORD_LIST *list;
  193. {
  194.   char *get_working_directory (), *getwd (), *directory;
  195.  
  196.   no_args (list);
  197.  
  198.   if (verbatim_pwd)
  199.     {
  200.       char *buffer = (char *)xmalloc (MAXPATHLEN);
  201.       directory = getwd (buffer);
  202.  
  203.       if (!directory)
  204.     {
  205.       builtin_error ("%s", buffer);
  206.       free (buffer);
  207.     }
  208.     }
  209.   else
  210.     {
  211.       directory = get_working_directory ("pwd");
  212.     }
  213.  
  214.   if (directory)
  215.     {
  216.       printf ("%s\n", directory);
  217.       fflush (stdout);
  218.       free (directory);
  219.       return (EXECUTION_SUCCESS);
  220.     }
  221.   else
  222.     return (EXECUTION_FAILURE);
  223. }
  224.  
  225. $BUILTIN pushd
  226. $FUNCTION pushd_builtin
  227. $DEPENDS_ON PUSHD_AND_POPD
  228. $SHORT_DOC pushd [dir | +n | -n]
  229. Adds a directory to the top of the directory stack, or rotates
  230. the stack, making the new top of the stack the current working
  231. directory.  With no arguments, exchanges the top two directories.
  232.  
  233. +n    Rotates the stack so that the Nth directory (counting
  234.     from the left of the list shown by `dirs') is at the top.
  235.  
  236. -n    Rotates the stack so that the Nth directory (counting
  237.     from the right) is at the top.
  238.  
  239. dir    adds DIR to the directory stack at the top, making it the
  240.     new current working directory.
  241.  
  242. You can see the directory stack with the `dirs' command.
  243. If the variable `pushd_silent' is not set and the pushd command
  244. was successful, a `dirs' is be performed as well.
  245. $END
  246.  
  247. #if defined (PUSHD_AND_POPD)
  248. /* Some useful commands whose behaviour has been observed in Csh. */
  249.  
  250. /* The list of remembered directories. */
  251. static char **pushd_directory_list = (char **)NULL;
  252.  
  253. /* Number of existing slots in this list. */
  254. static int directory_list_size = 0;
  255.  
  256. /* Offset to the end of the list. */
  257. static int directory_list_offset = 0;
  258.  
  259. pushd_builtin (list)
  260.      WORD_LIST *list;
  261. {
  262.   char *temp, *current_directory, *get_working_directory ();
  263.   int j = directory_list_offset - 1;
  264.   char direction = '+';
  265.  
  266.   /* If there is no argument list then switch current and
  267.      top of list. */
  268.   if (!list)
  269.     {
  270.       if (!directory_list_offset)
  271.     {
  272.       builtin_error ("No other directory");
  273.       return (EXECUTION_FAILURE);
  274.     }
  275.  
  276.       current_directory = get_working_directory ("pushd");
  277.       if (!current_directory)
  278.     return (EXECUTION_FAILURE);
  279.  
  280.       temp = pushd_directory_list[j];
  281.       pushd_directory_list[j] = current_directory;
  282.       goto change_to_temp;
  283.     }
  284.   else
  285.     {
  286.       direction = *(list->word->word);
  287.       if (direction == '+' || direction == '-')
  288.     {
  289.       int num;
  290.       if (1 == sscanf (&(list->word->word)[1], "%d", &num))
  291.         {
  292.           if (direction == '-')
  293.         num = directory_list_offset - num;
  294.  
  295.           if (num > directory_list_offset || num < 0)
  296.         {
  297.           if (!directory_list_offset)
  298.             builtin_error ("Directory stack empty");
  299.           else
  300.             builtin_error ("Stack contains only %d directories",
  301.                     directory_list_offset + 1);
  302.           return (EXECUTION_FAILURE);
  303.         }
  304.           else
  305.         {
  306.           /* Rotate the stack num times.  Remember, the
  307.              current directory acts like it is part of the
  308.              stack. */
  309.           temp = get_working_directory ("pushd");
  310.  
  311.           if (!num)
  312.             goto change_to_temp;
  313.  
  314.           do
  315.             {
  316.               char *top =
  317.             pushd_directory_list[directory_list_offset - 1];
  318.  
  319.               for (j = directory_list_offset - 2; j > -1; j--)
  320.             pushd_directory_list[j + 1] = pushd_directory_list[j];
  321.  
  322.               pushd_directory_list[j + 1] = temp;
  323.  
  324.               temp = top;
  325.               num--;
  326.             }
  327.           while (num);
  328.  
  329.           temp = savestring (temp);
  330.         change_to_temp:
  331.           {
  332.             int tt = EXECUTION_FAILURE;
  333.  
  334.             if (temp)
  335.               {
  336.             tt = cd_to_string (temp);
  337.             free (temp);
  338.               }
  339.  
  340.             if ((tt == EXECUTION_SUCCESS) &&
  341.             (!find_variable ("pushd_silent")))
  342.               dirs_builtin ((WORD_LIST *)NULL);
  343.  
  344.             return (tt);
  345.           }
  346.         }
  347.         }
  348.     }
  349.  
  350.       /* Change to the directory in list->word->word.  Save the current
  351.      directory on the top of the stack. */
  352.       current_directory = get_working_directory ("pushd");
  353.       if (!current_directory)
  354.     return (EXECUTION_FAILURE);
  355.  
  356.       if (cd_builtin (list) == EXECUTION_SUCCESS)
  357.     {
  358.       if (directory_list_offset == directory_list_size)
  359.         {
  360.           pushd_directory_list = (char **)
  361.         xrealloc (pushd_directory_list,
  362.               (directory_list_size += 10) * sizeof (char *));
  363.         }
  364.       pushd_directory_list[directory_list_offset++] = current_directory;
  365.  
  366.       if (!find_variable ("pushd_silent"))
  367.         dirs_builtin ((WORD_LIST *)NULL);
  368.  
  369.       return (EXECUTION_SUCCESS);
  370.     }
  371.       else
  372.     {
  373.       free (current_directory);
  374.       return (EXECUTION_FAILURE);
  375.     }
  376.     }
  377. }
  378. #endif /* PUSHD_AND_POPD */
  379.  
  380. $BUILTIN dirs
  381. $FUNCTION dirs_builtin
  382. $DEPENDS_ON PUSHD_AND_POPD
  383. $SHORT_DOC dirs [-l]
  384. Display the list of currently remembered directories.  Directories
  385. find their way onto the list with the `pushd' command; you can get
  386. back up through the list with the `popd' command.
  387.  
  388. The -l flag specifies that `dirs' should not print shorthand versions
  389. of directories which are relative to your home directory.  This means
  390. that `~/bin' might be displayed as `/homes/bfox/bin'.
  391. $END
  392.  
  393. #if defined (PUSHD_AND_POPD)
  394. /* Print the current list of directories on the directory stack. */
  395. dirs_builtin (list)
  396.      WORD_LIST *list;
  397. {
  398.   register int i, format = 0;
  399.   char *temp, *polite_directory_format (), *get_working_directory ();
  400.  
  401.   /* Maybe do long form? */
  402.   if (list)
  403.     {
  404.       if (strcmp (list->word->word, "-l") == 0)
  405.     format++;
  406.       else if (!format || list->next)
  407.     {
  408.       bad_option (list->word->word);
  409.       return (EXECUTION_FAILURE);
  410.     }
  411.     }
  412.  
  413.   /* The first directory printed is always the current working directory. */
  414.   temp = get_working_directory ("dirs");
  415.   if (!temp)
  416.     temp = savestring ("<no directory>");
  417.   printf ("%s", format ? temp : polite_directory_format (temp));
  418.   free (temp);
  419.  
  420.   /* Now print any directories in the array. */
  421.   for (i = (directory_list_offset - 1); i > -1; i--)
  422.     printf (" %s", format ? pushd_directory_list[i] :
  423.         polite_directory_format (pushd_directory_list[i]));
  424.  
  425.   putchar ('\n');
  426.   fflush (stdout);
  427.   return (EXECUTION_SUCCESS);
  428. }
  429. #endif /* PUSHD_AND_POPD */
  430.  
  431. $BUILTIN popd
  432. $FUNCTION popd_builtin
  433. $DEPENDS_ON PUSHD_AND_POPD
  434. $SHORT_DOC popd [+n | -n]
  435. Removes entries from the directory stack.  With no arguments,
  436. removes the top directory from the stack, and cd's to the new
  437. top directory.
  438.  
  439. +n    removes the Nth entry counting from the left of the list
  440.     shown by `dirs', starting with zero.  For example: `popd +0'
  441.     removes the first directory, `popd +1' the second.
  442.  
  443. -n    removes the Nth entry counting from the right of the list
  444.     shown by `dirs', starting with zero.  For example: `popd -0'
  445.     removes the last directory, `popd -1' the next to last.
  446.  
  447. You can see the directory stack with the `dirs' command.
  448. If the variable 'pushd_silent' is not set and the popd command
  449. was successful, a 'dirs' will be performed as well..
  450. $END
  451.  
  452. #if defined (PUSHD_AND_POPD)
  453. /* Pop the directory stack, and then change to the new top of the stack.
  454.    If LIST is non-null it should consist of a word +N or -N, which says
  455.    what element to delete from the stack.  The default is the top one. */
  456. popd_builtin (list)
  457.      WORD_LIST *list;
  458. {
  459.   register int i;
  460.   int which = 0;
  461.   char direction = '+';
  462.  
  463.   if (list)
  464.     {
  465.       direction = *(list->word->word);
  466.  
  467.       if ((direction != '+' && direction != '-') ||
  468.       (1 != sscanf (&((list->word->word)[1]), "%d", &which)))
  469.     {
  470.       builtin_error ("bad arg `%s'", list->word->word);
  471.       return (EXECUTION_FAILURE);
  472.     }
  473.     }
  474.  
  475.   if (which > directory_list_offset || (!directory_list_offset && !which))
  476.     {
  477.       if (!directory_list_offset)
  478.     builtin_error ("Directory stack empty");
  479.       else
  480.     builtin_error ("Stack contains only %d directories",
  481.             directory_list_offset + 1);
  482.       return (EXECUTION_FAILURE);
  483.     }
  484.  
  485.   /* Handle case of no specification, or top of stack specification. */
  486.   if ((direction == '+' && which == 0) ||
  487.       (direction == '-' && which == directory_list_offset))
  488.     {
  489.       i = cd_to_string (pushd_directory_list[directory_list_offset - 1]);
  490.       if (i != EXECUTION_SUCCESS)
  491.     return (i);
  492.       free (pushd_directory_list[--directory_list_offset]);
  493.     }
  494.   else
  495.     {
  496.       /* Since an offset other than the top directory was specified,
  497.      remove that directory from the list and shift the remainder
  498.      of the list into place. */
  499.  
  500.       if (direction == '+')
  501.     i = directory_list_offset - which;
  502.       else
  503.     i = which;
  504.  
  505.       free (pushd_directory_list[i]);
  506.       directory_list_offset--;
  507.  
  508.       /* Shift the remainder of the list into place. */
  509.       for (; i < directory_list_offset; i++)
  510.     pushd_directory_list[i] = pushd_directory_list[i + 1];
  511.     }
  512.  
  513.   if (!find_variable ("pushd_silent"))
  514.     dirs_builtin ((WORD_LIST *)NULL);
  515.  
  516.   return (EXECUTION_SUCCESS);
  517. }
  518. #endif /* PUSHD_AND_POPD */
  519.  
  520. /* Do the work of changing to the directory NEWDIR.  Handle symbolic
  521.    link following, etc. */
  522. static int
  523. change_to_directory (newdir)
  524.      char *newdir;
  525. {
  526.   char *get_working_directory (), *make_absolute ();
  527.   char *t;
  528.  
  529.   if (follow_symbolic_links)
  530.     {
  531.       if (!the_current_working_directory)
  532.     {
  533.       t = get_working_directory ("cd_links");
  534.       if (t)
  535.         free (t);
  536.     }
  537.  
  538.       if (the_current_working_directory)
  539.     t = make_absolute (newdir, the_current_working_directory);
  540.       else
  541.     t = savestring (newdir);
  542.  
  543.       /* Get rid of trailing `/'. */
  544.       {
  545.     register int len_t = strlen (t);
  546.     if (len_t > 1)
  547.       {
  548.         --len_t;
  549.         if (t[len_t] == '/')
  550.           t[len_t] = '\0';
  551.       }
  552.       }
  553.  
  554.       if (chdir (t) < 0)
  555.     {
  556.       free (t);
  557.       return (0);
  558.     }
  559.  
  560.       if (the_current_working_directory)
  561.     strcpy (the_current_working_directory, t);
  562.  
  563.       free (t);
  564.       return (1);
  565.     }
  566.   else
  567.     {
  568.       if (chdir (newdir) < 0)
  569.     return (0);
  570.       else
  571.     return (1);
  572.     }
  573. }
  574.  
  575. /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
  576.    so if the result is EXECUTION_FAILURE then an error message has already
  577.    been printed. */
  578. static int
  579. cd_to_string (name)
  580.      char *name;
  581. {
  582.   extern WORD_LIST *make_word_list ();
  583.   WORD_LIST *tlist = make_word_list (make_word (name), NULL);
  584.   int result = (cd_builtin (tlist));
  585.   dispose_words (tlist);
  586.   return (result);
  587. }
  588.  
  589.